package com.streamer.app.widget

import android.content.Context
import android.graphics.*
import android.text.TextPaint
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import kotlin.math.abs

class WhiteboardDrawingView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {

    enum class ToolMode { DRAW, ERASE, PAN, LINE, TEXT, SHAPE, ERASE_WHITE_CIRCLE }

    private var toolMode: ToolMode = ToolMode.DRAW

    private val drawPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        style = Paint.Style.STROKE
        strokeWidth = 6f
        color = Color.BLACK
        strokeCap = Paint.Cap.ROUND
        strokeJoin = Paint.Join.ROUND
    }

    private val erasePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        style = Paint.Style.STROKE
        strokeWidth = 24f
        xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
        strokeCap = Paint.Cap.ROUND
        strokeJoin = Paint.Join.ROUND
    }

    // Note: Default eraser uses CLEAR xfermode to reveal background
    private val clearCirclePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        style = Paint.Style.FILL
        xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
    }
    private var circleEraserRadius = 18f

    private val textPaint = TextPaint(Paint.ANTI_ALIAS_FLAG).apply {
        color = Color.BLACK
        textSize = 42f
    }

    private data class DrawPath(val path: Path, val paint: Paint)
    private data class DrawText(val text: String, val x: Float, val y: Float, val paint: TextPaint)

    private val paths: MutableList<DrawPath> = mutableListOf()
    private val texts: MutableList<DrawText> = mutableListOf()

    private var currentPath: Path? = null
    private var bufferBitmap: Bitmap? = null
    private var bufferCanvas: Canvas? = null

    private var lastX = 0f
    private var lastY = 0f

    // Panning offsets
    private var offsetX = 0f
    private var offsetY = 0f

    // For line tool
    private var lineStartX = 0f
    private var lineStartY = 0f
    private var isDrawingLine = false

    // For TEXT placement: when in TEXT mode, next tap position will be used
    private var onRequestText: ((Float, Float) -> Unit)? = null

    fun setMode(mode: ToolMode) {
        toolMode = mode
        invalidate()
    }
    
    fun setStrokeWidth(width: Float) {
        drawPaint.strokeWidth = width
        invalidate()
    }

    fun getStrokeWidth(): Float = drawPaint.strokeWidth

    fun setStrokeColor(color: Int) {
        drawPaint.color = color
        invalidate()
    }

    fun getStrokeColor(): Int = drawPaint.color

    fun setEraserWidth(width: Float) {
        erasePaint.strokeWidth = width
        invalidate()
    }

    fun getEraserWidth(): Float = erasePaint.strokeWidth

    // keep API stable if called elsewhere; they do nothing for CLEAR-based eraser
    fun setEraserAsWhiteCircle(enabled: Boolean) { /* no-op for CLEAR eraser */ }
    fun setEraserRadius(radius: Float) { /* no-op for CLEAR eraser */ }

    fun setWhiteEraserRadius(radius: Float) {
        circleEraserRadius = radius
        invalidate()
    }

    fun setOnRequestText(callback: ((x: Float, y: Float) -> Unit)?) {
        onRequestText = callback
    }

    fun addTextAt(text: String, x: Float, y: Float) {
        texts.add(DrawText(text, x - offsetX, y - offsetY, TextPaint(textPaint)))
        redrawBuffer()
        invalidate()
    }

    fun clearAll() {
        paths.clear()
        texts.clear()
        redrawBuffer()
        invalidate()
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        if (w > 0 && h > 0) {
            bufferBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
            bufferCanvas = Canvas(bufferBitmap!!).apply {
                drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
            }
            redrawBuffer()
        }
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)

        canvas.save()
        canvas.translate(offsetX, offsetY)
        bufferBitmap?.let { canvas.drawBitmap(it, 0f, 0f, null) }

        // Preview current path or line while drawing
        currentPath?.let { path ->
            val p = if (toolMode == ToolMode.ERASE) erasePaint else drawPaint
            canvas.drawPath(path, p)
        }
        if (isDrawingLine) {
            val previewPaint = Paint(drawPaint)
            when (toolMode) {
                ToolMode.LINE -> canvas.drawLine(lineStartX, lineStartY, lastX - offsetX, lastY - offsetY, previewPaint)
                ToolMode.SHAPE -> {
                    val path = buildDiamondPath(lineStartX, lineStartY, lastX - offsetX, lastY - offsetY)
                    canvas.drawPath(path, previewPaint)
                }
                else -> {}
            }
        }

        canvas.restore()
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        val x = event.x
        val y = event.y
        when (event.actionMasked) {
            MotionEvent.ACTION_DOWN -> handleActionDown(x, y)
            MotionEvent.ACTION_MOVE -> handleActionMove(x, y)
            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> handleActionUp(x, y)
        }
        return true
    }

    private fun handleActionDown(x: Float, y: Float) {
        lastX = x
        lastY = y
        when (toolMode) {
            ToolMode.DRAW -> {
                currentPath = Path().apply { moveTo(x - offsetX, y - offsetY) }
            }
            ToolMode.ERASE -> {
                currentPath = Path().apply { moveTo(x - offsetX, y - offsetY) }
            }
            ToolMode.ERASE_WHITE_CIRCLE -> {
                // Do not erase on tap; only erase when sliding in ACTION_MOVE
            }
            ToolMode.PAN -> {
                // start pan
            }
            ToolMode.LINE, ToolMode.SHAPE -> {
                lineStartX = x - offsetX
                lineStartY = y - offsetY
                isDrawingLine = true
            }
            ToolMode.TEXT -> {
                onRequestText?.invoke(x, y)
            }
        }
        invalidate()
    }

    private fun handleActionMove(x: Float, y: Float) {
        when (toolMode) {
            ToolMode.DRAW -> currentPath?.lineTo(x - offsetX, y - offsetY)
            ToolMode.ERASE -> currentPath?.lineTo(x - offsetX, y - offsetY)
            ToolMode.ERASE_WHITE_CIRCLE -> {
                // Erase only when sliding: interpolate stamps along the path
                val prevX = lastX - offsetX
                val prevY = lastY - offsetY
                val currX = x - offsetX
                val currY = y - offsetY
                val dx = currX - prevX
                val dy = currY - prevY
                val dist = kotlin.math.sqrt(dx * dx + dy * dy)
                // Minimum movement threshold to avoid accidental dots
                if (dist >= 2f) {
                    val step = (circleEraserRadius * 0.6f).coerceAtLeast(2f)
                    val steps = kotlin.math.max(1, (dist / step).toInt())
                    val stepX = dx / steps
                    val stepY = dy / steps
                    var sx = prevX
                    var sy = prevY
                    for (i in 1..steps) {
                        sx += stepX
                        sy += stepY
                        val path = Path().apply { addCircle(sx, sy, circleEraserRadius, Path.Direction.CW) }
                        paths.add(DrawPath(path, Paint(clearCirclePaint)))
                    }
                    redrawBuffer()
                }
            }
            ToolMode.PAN -> {
                val dx = x - lastX
                val dy = y - lastY
                if (abs(dx) > 0.1f || abs(dy) > 0.1f) {
                    offsetX += dx
                    offsetY += dy
                }
            }
            ToolMode.LINE, ToolMode.SHAPE -> { /* preview handled in onDraw */ }
            ToolMode.TEXT -> {}
        }
        lastX = x
        lastY = y
        invalidate()
    }

    private fun handleActionUp(x: Float, y: Float) {
        when (toolMode) {
            ToolMode.DRAW -> finalizeCurrentPath(drawPaint)
            ToolMode.ERASE -> finalizeCurrentPath(erasePaint)
            ToolMode.ERASE_WHITE_CIRCLE -> { /* all stamped already */ }
            ToolMode.PAN -> { /* no-op */ }
            ToolMode.LINE -> {
                if (isDrawingLine) {
                    val path = Path().apply {
                        moveTo(lineStartX, lineStartY)
                        lineTo(x - offsetX, y - offsetY)
                    }
                    paths.add(DrawPath(path, Paint(drawPaint)))
                    isDrawingLine = false
                    redrawBuffer()
                }
            }
            ToolMode.SHAPE -> {
                if (isDrawingLine) {
                    val path = buildDiamondPath(lineStartX, lineStartY, x - offsetX, y - offsetY)
                    paths.add(DrawPath(path, Paint(drawPaint)))
                    isDrawingLine = false
                    redrawBuffer()
                }
            }
            ToolMode.TEXT -> { /* handled on down */ }
        }
        currentPath = null
        invalidate()
    }

    private fun buildDiamondPath(x0: Float, y0: Float, x1: Float, y1: Float): Path {
        val cx = (x0 + x1) / 2f
        val cy = (y0 + y1) / 2f
        val halfW = abs(x1 - x0) / 2f
        val halfH = abs(y1 - y0) / 2f
        return Path().apply {
            moveTo(cx, cy - halfH) // top
            lineTo(cx + halfW, cy) // right
            lineTo(cx, cy + halfH) // bottom
            lineTo(cx - halfW, cy) // left
            close()
        }
    }

    private fun finalizeCurrentPath(paintTemplate: Paint) {
        currentPath?.let { p ->
            val paintCopy = Paint(paintTemplate)
            paths.add(DrawPath(Path(p), paintCopy))
            redrawBuffer()
        }
    }

    private fun redrawBuffer() {
        val canvas = bufferCanvas ?: return
        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
        // Draw existing paths and texts
        for (dp in paths) {
            canvas.drawPath(dp.path, dp.paint)
        }
        for (t in texts) {
            canvas.drawText(t.text, t.x, t.y, t.paint)
        }
    }
}


